﻿using Architecture.CrossCutting.Common;
using Architecture.CrossCutting.Common.Logging;
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Threading.Tasks;
using System.Web;
using System.Collections.ObjectModel;

namespace Architecture.CrossCutting.Web.WCF
{

    public class PipelineDiagnosticService : IDispatchMessageInspector, IParameterInspector, IServiceBehavior
    {

        IGenericLogger<WcfServiceLog> _logger;

        public PipelineDiagnosticService(IGenericLogger<WcfServiceLog> logger)
        {
            _logger = logger;
            this.Extension = new PipelineExtension<WcfServiceLog>();
        }

        public PipelineExtension<WcfServiceLog> Extension { get; set; }

        public Boolean IsFullLog { get; set; }
        public Boolean IsEnabled { get; set; }
        public Boolean LogBeforeStep { get; set; }

        public Boolean LogAfterStep { get; set; }

        public object BeforeCall(string operationName, object[] inputs)
        {
            WcfServiceLog log = null;
            Boolean fullPipelineInpsection = true;
            var pipelineExtension = OperationContext.Current.Extensions.Find<PipelineExtension<WcfServiceLog>>();

            if (pipelineExtension != null)
            {
                log = pipelineExtension.LogDetail;
                log.UpdateExecutionContext();
            }

            if (log == null)
            {
                fullPipelineInpsection = false;
                log = new WcfServiceLog();
                log.UpdateExecutionContext();
            }

            //Set properties
            log.SetBeforeCallOperation(operationName);

            if (this.IsFullLog || this.LogBeforeStep)
            {
                System.Threading.Tasks.Task.Run(() =>
                {
                    _logger.WriteInfo(String.Format("Pipeline Step 3 : BeforeSendRequest - Correlation Id '{0}'", 1),
                                      log);
                }).ContinueWith(completed =>
                {
                    switch (completed.Status)
                    {
                        case TaskStatus.Faulted:
                            {
                                _logger.WriteError(String.Format("Pipeline Step 3 : BeforeSendRequest - Correlation Id '{0}'", 1),
                                      log,
                                    completed.Exception.InnerException);
                                break;
                            }
                    }

                });
            }
            System.Diagnostics.Stopwatch watcher = new System.Diagnostics.Stopwatch();
            watcher.Start();

            return new RequestReplyCorrelation<WcfServiceLog>()
            {
                Watcher = watcher,
                LogDetail = log
            };

        }

        public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
        {
            RequestReplyCorrelation<WcfServiceLog> correlationRequest = (correlationState as RequestReplyCorrelation<WcfServiceLog>);
            if (correlationRequest == null)
            {
                throw new NotImplementedException("Manage this case or Not which never shouldn't fire");
            }

            //Stop the watcher
            correlationRequest.Watcher.Stop();

            //Complete the request Data
            correlationRequest.LogDetail.SetAfterCallOperation();

            if (this.IsFullLog || this.LogAfterStep)
            {
                Task.Run(() =>
                {
                    _logger.WriteInfo(String.Format("Pipeline Step 3 : AfterReceiveReply - Correlation Id '{0}'", 1), correlationRequest.LogDetail);
                }).ContinueWith(completed =>
                {
                    switch (completed.Status)
                    {
                        case TaskStatus.Faulted:
                            {
                                _logger.WriteError(String.Format("Pipeline Step 3 : AfterReceiveReply - Correlation Id '{0}'", 1),
                                      correlationRequest.LogDetail,
                                    completed.Exception.InnerException);
                                break;
                            }
                    }
                });
            }

            var pipelineExtension = OperationContext.Current.Extensions.Find<PipelineExtension<WcfServiceLog>>();

            if (pipelineExtension != null)
            {
                pipelineExtension.LogDetail = correlationRequest.LogDetail;
            }

        }


        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            WcfServiceLog log = new WcfServiceLog();
            log.RegisterRequest(request);

            if (this.IsFullLog || this.LogBeforeStep)
            {

                System.Threading.Tasks.Task.Run(() =>
                {
                    _logger.WriteInfo(String.Format("Pipeline Step 1 : BeforeCall - Correlation Id '{0}'", 1),
                                      log);
                }).ContinueWith(completed =>
                {
                    switch (completed.Status)
                    {
                        case TaskStatus.Faulted:
                            {
                                _logger.WriteError(String.Format("Pipeline Step 1 : BeforeCall - Correlation Id '{0}'", 1),
                                      log,
                                    completed.Exception.InnerException);
                                break;
                            }
                    }

                });
            }

            var pipelineExtension = OperationContext.Current.Extensions.Find<PipelineExtension<WcfServiceLog>>();

            if (pipelineExtension != null)
            {
                pipelineExtension.LogDetail = log;
            }


            System.Diagnostics.Stopwatch watcher = new System.Diagnostics.Stopwatch();
            watcher.Start();

            return new RequestReplyCorrelation<WcfServiceLog>()
            {
                Watcher = watcher,
                LogDetail = log
            };
        }

        public void BeforeSendReply(ref Message reply, object correlationState)
        {
            if (!this.IsEnabled) { return; }

            RequestReplyCorrelation<WcfServiceLog> correlationRequest = (correlationState as RequestReplyCorrelation<WcfServiceLog>);
            if (correlationRequest == null)
            {
                throw new NotImplementedException("Manage this case or Not which never shouldn't fire");
            }

            //Stop the watcher
            correlationRequest.Watcher.Stop();

            var log = correlationRequest.LogDetail;
            log.UpdateExecutionContext();

            //Complete the request Data
            log.RegisterResponse(reply);

            Task.Run(() =>
            {
                _logger.WriteInfo(String.Format("Pipeline Step 1 : AfterCall - Correlation Id '{0}'", 1), correlationRequest.LogDetail);
            }).ContinueWith(completed =>
            {
                switch (completed.Status)
                {
                    case TaskStatus.Faulted:
                        {
                            try
                            {
                                _logger.WriteError(String.Format("Pipeline Step 1 : AfterCall - Correlation Id '{0}'", 1),
                                        correlationRequest.LogDetail,
                                    completed.Exception.InnerException);
                            }
                            catch (Exception ex)
                            {
                                System.Diagnostics.EventLog eventLog = new System.Diagnostics.EventLog("Application");
                                eventLog.Source = "PipelineDiagnosticClient";
                                eventLog.WriteEntry(ex.ToString());
                            }
                            break;
                        }
                }
            });

        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher channelDispatch in serviceHostBase.ChannelDispatchers)
            {
                foreach (EndpointDispatcher endpointDispatch in channelDispatch.Endpoints)
                {
                    endpointDispatch.DispatchRuntime.MessageInspectors.Add(this);
                    foreach(DispatchOperation operation in endpointDispatch.DispatchRuntime.Operations)
                    {
                        operation.ParameterInspectors.Add(this);
                    }
                }
            }
        }
    }
}
